/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using ExcelDataReader;

namespace DouEditor.ConfigParser
{
    /// <summary>
    /// Excel 配置解析类
    /// 1. 仅包含基本解析，不包含数据解析；
    /// 2. 请按需修改扩展当前解析类，这里仅提供最基础的解析逻辑；
    /// 3. 这里仅提供配置解析的功能，具体导出请按需编写；
    /// 4. 配置格式请参考 ConfigTemplate.xlsx 文件；
    /// </summary>
    public static class ExcelParser
    {
        // [UnityEditor.MenuItem("DouFramework/测试/ExcelParser测试")]
        // public static void Test()
        // {
        //     var configList = new List<ConfigInfo>();
        //     if (!ExcelParser.Parser("Assets/DouFrameworkEditor/ConfigParser/ConfigTemplate.xlsx", configList, out var error))
        //     {
        //         UnityEngine.Debug.LogError(error);
        //         return;
        //     }
        //     UnityEngine.Debug.LogError("success");
        // }
        
        private static readonly Regex FetchSheetName = new Regex(@"^[a-zA-Z][a-zA-Z0-9]*$", RegexOptions.Singleline);
        private static readonly Regex FetchFieldName = new Regex(@"^[a-zA-Z][a-zA-Z0-9_]*$", RegexOptions.Singleline);
        private static readonly Regex FetchComments = new Regex(@"^\s*#.*$", RegexOptions.Singleline);
        
        public static bool Parser(string excelFilePath, List<ConfigInfo> result, out string error)
        {
            try
            {
                // FileShare.ReadWrite 支持 Excel 在编辑状态下读取数据
                using (var stream = File.Open(excelFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    using (var reader = ExcelReaderFactory.CreateReader(stream))
                    {
                        do
                        {
                            if (FetchSheetName.IsMatch(reader.Name))
                            {
                                var config = new ConfigInfo();
                                config.filePath = excelFilePath;
                                config.fileName = Path.GetFileName(excelFilePath);
                                config.sheetName = reader.Name;
                                
                                // 略过空行和注释行
                                var isEmpty = true;
                                while (reader.Read()) // 读取一行
                                {
                                    isEmpty = false;
                                    // 只判断第一列的数据
                                    Object obj = null;
                                    try
                                    {
                                        obj = reader.GetValue(0); // 读取第一列
                                    }
                                    catch (Exception e)
                                    {
                                        error = $"文件\"{excelFilePath}\"的\"{reader.Name}\"是空表无数据！";
                                        return false;
                                    }
                                    var str = obj == null ? String.Empty : obj.ToString();
                                    if (!string.IsNullOrWhiteSpace(str) && !FetchComments.IsMatch(str))
                                    {
                                        break;
                                    }
                                }

                                if (isEmpty)
                                {
                                    error = $"文件\"{excelFilePath}\"的\"{reader.Name}\"是空表无数据！";
                                    return false;
                                }
                                
                                // 开始读取数据
                                var row = 1;
                                do
                                {
                                    for (int col = 1; col <= reader.FieldCount; col++) // 读取每列
                                    {
                                        var obj = reader.GetValue(col - 1);
                                        var str = obj == null ? String.Empty : obj.ToString();

                                        if (col == 1)
                                        {
                                            // 第一行的第一列数据作为配置表描述
                                            if (row == 1)
                                            {
                                                config.sheetDesc = str;
                                            }
                                            
                                            // 读取第一列是否存在注释符号
                                            if (row > 5)
                                            {
                                                config.rowComments.Add(FetchComments.IsMatch(str));
                                            }

                                            // 第一列的数据跳过获取
                                            continue;
                                        }

                                        if (row == 1)
                                        {
                                            config.fields.Add(new Field());
                                        }

                                        var field = config.fields[col - 2];
                                        switch (row)
                                        {
                                            case 1: // 额外信息
                                                ParseExtra(config, field, row, col, str);
                                                break;
                                            case 2: // 字段名
                                                field.field.name = str;
                                                break;
                                            case 3: // 字段类型
                                                field.field.type = str;
                                                break;
                                            case 4: // 备注
                                                var desc = str.Replace("\r", @"\r");
                                                desc = desc.Replace("\n", @"\n");
                                                desc = desc.Replace("\r\n", @"\r\n");
                                                field.field.desc = desc;
                                                break;
                                            case 5: // 导出类型
                                                ParseExportType(config, field, row, col, str);
                                                break;
                                            default:
                                                field.dataList.Add(str);
                                                break;
                                        }
                                    }

                                    row++;
                                }
                                while (reader.Read()); // 读取一行
                                
                                if (FormatCheck(config, out error))
                                {
                                    result.Add(config);
                                }
                                else
                                {
                                    return false;
                                }
                            }
                        }
                        while (reader.NextResult());
                    }
                }
            }
            catch (IOException e)
            {
                error = $"文件\"{excelFilePath}\"IO错误：{e.Message}\r{e.StackTrace}";
                return false;
            }
            catch (Exception e)
            {
                error = $"文件\"{excelFilePath}\"解析报错：{e.Message}\r{e.StackTrace}";
                return false;
            }
            
            error = String.Empty;
            return true;
        }

        /// <summary>
        /// 解析预留数据行
        /// </summary>
        private static void ParseExtra(ConfigInfo config, Field field, int row, int col, string str)
        {
        }

        /// <summary>
        /// 解析导出类型
        /// </summary>
        private static void ParseExportType(ConfigInfo config, Field field, int row, int col, string str)
        {
            if (string.IsNullOrWhiteSpace(str))
            {
                field.field.exportType = FieldExportType.Client | FieldExportType.Server;
            }
            else
            {
                var lower = str.ToLower();
                if (lower == "key")
                {
                    field.field.exportType |= FieldExportType.Key;
                }
                else
                {
                    if (lower.Contains("c"))
                    {
                        field.field.exportType |= FieldExportType.Client;
                    }
                    if (lower.Contains("s"))
                    {
                        field.field.exportType |= FieldExportType.Server;
                    }
                }
            }
        }

        /// <summary>
        /// 格式检测
        /// </summary>
        private static bool FormatCheck(ConfigInfo config, out string error)
        {
            // 去掉缺少字段名称和字段类型的字段
            {
                for (int i = 0; i < config.fields.Count; i++)
                {
                    var cfg = config.fields[i];
                    if (string.IsNullOrWhiteSpace(cfg.field.name) || string.IsNullOrWhiteSpace(cfg.field.type))
                    {
                        config.fields.RemoveAt(i);
                        i--;
                    }
                }
            }
            
            // 处理空行
            {
                var fields = config.GetFields(FieldExportType.All);
                for (int i = 0; i < config.dataCount; i++)
                {
                    var empty = true;
                    for (int j = 0; j < fields.Count; j++)
                    {
                        var field = fields[j];
                        var content = field.dataList[i];
                        if (!string.IsNullOrWhiteSpace(content))
                        {
                            empty = false;
                        }
                    }
                    if (empty)
                    {
                        for (int j = 0; j < fields.Count; j++)
                        {
                            var field = fields[j];
                            field.dataList.RemoveAt(i);
                        }
                        config.rowComments.RemoveAt(i);
                        i--;
                    }
                }
            }
            
            // 处理注释行
            {
                var fields = config.GetFields(FieldExportType.All);
                for (int i = 0; i < config.dataCount; i++)
                {
                    if (config.rowComments[i])
                    {
                        for (int j = 0; j < fields.Count; j++)
                        {
                            var field = fields[j];
                            field.dataList.RemoveAt(i);
                        }
                        config.rowComments.RemoveAt(i);
                        i--;
                    }
                }
            }
            
            // 字段名检查，小写数字加下划线，不能有大写
            {
                var fields = config.GetFields(FieldExportType.All);
                foreach (var field in fields)
                {
                    var name = field.field.name;
                    if (!FetchFieldName.IsMatch(name))
                    {
                        error = $"文件\"{config.filePath}\"表\"{config.sheetName}\"字段名\"{name}\"不符合规范";
                        return false;
                    }
                }
            }
            
            // 字段类型检查
            {
                var fields = config.GetFields(FieldExportType.All);
                foreach (var field in fields)
                {
                    var type = field.field.type;
                    if (type != "bool"
                        && type != "int"
                        && type != "int[]"
                        && type != "int[][]"
                        && type != "float"
                        && type != "float[]"
                        && type != "float[][]"
                        && type != "string"
                        && type != "string[]"
                        && type != "string[][]"
                       )
                    {
                        error = $"文件\"{config.filePath}\"表\"{config.sheetName}\"字段名\"{field.field.name}\"的类型\"{type}\"错误";
                        return false;
                    }
                }
            }
            
            // key 标记检查
            {
                var fields = config.GetFields(FieldExportType.All);
                var keyCount = 0;
                foreach (var field in fields)
                {
                    if ((field.field.exportType & FieldExportType.Key) != 0)
                    {
                        keyCount++;
                    }
                }
                if (keyCount > 1)
                {
                    error = $"文件\"{config.filePath}\"表\"{config.sheetName}\"出现了多个被标记为键值(key)的字段";
                    return false;
                }
            }

            // 空表检查
            {
                if (config.dataCount == 0)
                {
                    error = $"文件\"{config.filePath}\"表\"{config.sheetName}\"配置数据为空";
                    return false;
                }
            }
            
            error = String.Empty;
            return true;
        }
    }
}
